25. 中国港口与外贸景气探索(印度数据作方法对照)

本章概要

  • 学习材料:中国港口企业财报与行情数据、印度外贸对照表。
  • 本章任务:整理季度营业收入和月度股票行情,并与印度贸易表按国家和年份进行比较。
  • 完成后你将得到:企业数据完整性说明、2015—2025 年单季营业收入、月度收益和两类案例的差异表。
  • 自我检查:核对累计转单季守恒与去年同季配对;不得把企业代理写成海关因果结论。
  • 拓展练习:把同一代理依据框架拓展应用到另一中国港口企业。

学习说明:本章只计“企业代理构造、核验与边界说明”的方法成果;港口企业财报/行情不具海关贸易领域等价性,不计真实外贸完成案例。

贸易数据分析的重要性

对外贸易分析是理解国际经济格局的核心手段:

  • 贸易伙伴:识别主要进出口国及其贸易地位
  • 贸易趋势:了解贸易发展方向与周期变化
  • 政策制定:基于数据驱动的贸易政策决策
  • 产业结构:揭示经济结构与产业竞争力

中国主案例:长三角港口企业景气验证

要素 课堂口径
本地资产 stock/financial_statement.h5stock/stock_price_pre_adjusted.h5
对象与期间 宁波港 601018.SH、上港集团 600018.SH,2015–2025 年
指标 季度营业收入/净利润增速、月度复权收益率
决策 连续两季增长转负时,运营与投资负责人启动情景核对

代理边界与决策含义

港口公司指标仅作景气代理,不能替代海关贸易总额;印度表只用于算法对照。

中国主案例:本地数据准备条件

Listing 1: 长三角港口企业本地数据要求
展开中国港口主案例数据要求代码
from pathlib import Path
import pandas as pd

linux_root = Path('/home/ubuntu/r2_data_mount/data')
windows_root = Path(r'C:\qiufei\data')
data_root = linux_root if linux_root.exists() else windows_root
financial_path = data_root / 'stock' / 'financial_statement.h5'
price_path = data_root / 'stock' / 'stock_price_pre_adjusted.h5'
target_codes = ['601018.SH', '600018.SH']
Listing 2: 长三角港口企业数据就绪状态
展开数据就绪代码
if financial_path.exists() and price_path.exists():
    rq_codes = [code.replace('.SH','.XSHG').replace('.SZ','.XSHE') for code in target_codes]  # 转换为HDF证券代码
    financials = pd.read_hdf(financial_path, key='financial_data', where='order_book_id in rq_codes', columns=['order_book_id','quarter','operating_revenue','net_profit']).rename(columns={'order_book_id':'ts_code','quarter':'end_date','operating_revenue':'total_revenue','net_profit':'n_income'})  # 按目标公司读取财报
    prices = pd.read_hdf(price_path, key='data', where='order_book_id in rq_codes', columns=['close']).reset_index().rename(columns={'order_book_id':'ts_code','date':'trade_date'})  # 按目标公司读取行情
    for frame in (financials, prices): frame['ts_code'] = frame['ts_code'].str.replace('.XSHG','.SH',regex=False).str.replace('.XSHE','.SZ',regex=False)  # 统一课堂证券代码后缀
    financials = financials.loc[financials['ts_code'].isin(target_codes)].copy()
    prices = prices.loc[prices['ts_code'].isin(target_codes)].copy()
    available = set(financials['ts_code']) & set(prices['ts_code'])
    missing = sorted(set(target_codes) - available)
    if missing:
        raise ValueError(f'财报或行情中缺少主案例企业 {missing},请核对下载文件和股票代码')
    financials['end_date'] = pd.PeriodIndex(financials['end_date'].str.upper(), freq='Q').end_time.normalize()  # 将季度键转换为报告期末日期
    prices['trade_date'] = pd.to_datetime(prices['trade_date'])
    financials = financials.loc[financials['end_date'].between('2015-01-01', '2025-12-31')]
    prices = prices.loc[prices['trade_date'].between('2015-01-01', '2025-12-31')]
    if financials.empty or prices.empty:
        raise ValueError('主案例期间内没有记录,请核对日期范围和下载文件')
    print({'status': 'READY', 'companies': target_codes,
           'financial_rows': len(financials), 'price_rows': len(prices),
           'next': '核验同期版本后计算季度增长与月度收益率'})
else:
    print({'status': '请检查数据', 'missing_files': [
        str(path) for path in [financial_path, price_path] if not path.exists()
    ], 'result_policy': '不构造增长率、收益率或决策结论'})
{'status': 'READY', 'companies': ['601018.SH', '600018.SH'], 'financial_rows': 88, 'price_rows': 5348, 'next': '核验同期版本后计算季度增长与月度收益率'}

方法对照:读取印度进出口数据

展开完整代码(投影默认折叠)

# 注:sk2018-2010_export.csv 和 sk2018-2010_import.csv 数据文件本地没有,但平台已经内置
# =============================================================================
# 题目:印度对外贸易数据读取与透视表构建
# =============================================================================
# 本代码块实现印度进出口贸易数据的读取和初步整理
# 通过透视表构建,将数据按国家和年份进行重排,便于后续趋势分析

# ==================== 导入数据分析与可视化库 ====================
import pandas as pd  # 导入Pandas库,用于数据表操作和分析
import numpy as np  # 导入NumPy库,用于数值计算
import matplotlib.pyplot as plt  # 导入Matplotlib库,用于绘图
from IPython.display import display, Markdown  # 导入显示工具,用于富文本输出

# ==================== 设置中文字体支持 ====================
plt.rcParams['font.sans-serif'] = ['Source Han Serif SC']  # 使用课程规定且已安装的思源宋体避免中文方框
plt.rcParams['axes.unicode_minus'] = False  # 解决负号'-'显示为方块的问题

# ==================== 读取进出口贸易数据 ====================
df_export = pd.read_csv('sk2018-2010_export.csv')  # 读取2010-2018年印度出口数据
df_import = pd.read_csv('sk2018-2010_import.csv')  # 读取2010-2018年印度进口数据

print('出口数据形状:', df_export.shape)  # 输出出口数据集的行数和列数
print('进口数据形状:', df_import.shape)  # 输出进口数据集的行数和列数

数据准备:印度平台表对照路线

数据说明sk2018-2010_export.csvsk2018-2010_import.csv 由教学平台提供。使用时应确认 country/year/value 字段和计量单位,再制作透视表和趋势图。

展开完整代码(投影默认折叠)

# ==================== 构建出口透视表 ====================
exp_country_table = pd.pivot_table(
    df_export,  # 以出口数据为源数据
    values='value',  # 透视表的值为贸易额(value字段)
    index='country',  # 行索引为国家,便于观察各国家趋势
    columns='year'  # 列为年份,便于观察时间序列变化
)

# ==================== 构建进口透视表 ====================
imp_country_table = pd.pivot_table(
    df_import,  # 以进口数据为源数据
    values='value',  # 透视表的值为贸易额(value字段)
    index='country',  # 行索引为国家
    columns='year'  # 列为年份
)

print('出口透视表:')
print(exp_country_table.head())  # 显示出口透视表的前5行,预览数据结构

运行前预测|出口趋势分析(平台任务)

  • 输入预测:运行前先写出 df_exportdf_importexp_country_tableimp_country_table 的业务含义、数据类型或取值范围,并判断哪一个输入最可能改变结果。
  • 结果预测:不展开答案,先预测将得到g3 的结果;同时写出方向、数量级或表格/图形结构。
  • 完成要求:能独立说明本任务从输入到“出口趋势分析(平台任务)”结果的关键步骤,原样录入平台代码并得到可核对的运行结果。

⭐ 出口趋势分析(平台任务)

展开受保护平台代码(默认收起以适配投影)
# 注:sk2018-2010_export.csv 和 sk2018-2010_import.csv 数据文件本地没有,但平台已经内置
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import pandas as pd  # 导入Pandas数据分析库
import numpy as np  # 导入NumPy数值计算库
import matplotlib.pyplot as plt  # 导入Matplotlib绘图库
plt.rcParams['font.sans-serif'] = ['SimHei'] # 使用黑体作为默认字体以支持中文
plt.rcParams['axes.unicode_minus'] = False # 正确显示负号
from IPython.display import display, Markdown  # 导入display,模块

#定义加粗显示函数
def bold(string):
  display(Markdown(string))  # 以Markdown格式渲染输出描述性文本
   
#读取进出口数据
df_export = pd.read_csv('sk2018-2010_export.csv')
df_import = pd.read_csv('sk2018-2010_import.csv')  # 从CSV文件读取数据存入df_import
# 创建透视表exp_country_table
exp_country_table = pd.pivot_table(df_export, values = 'value', index = 'country', columns = 'year')
# 创建透视表imp_country_table
imp_country_table = pd.pivot_table(df_import, values = 'value', index = 'country', columns = 'year')

bold('**2010年至2018年印度对外贸易出口方向的趋势**')  # 以粗体Markdown格式输出:2010年至2018年印度对外贸易出口方向的趋势
plt.figure(figsize=(15,10))  # 创建图形画布
 
plt.subplot(221)  # 选择子图位置
g = exp_country_table.loc["U S A"].plot(color='purple', linewidth=3)  # 按标签索引提取数据
g.set_ylabel('USD (millions)', fontsize = 12)  # 设置Y轴标签
g.set_xlabel('Year', fontsize = 12)  # 设置X轴标签
g.set_title('对美国出口的趋势', size = 15)  # 设置图表标题

plt.subplot(222)  # 选择子图位置
g1 = exp_country_table.loc["U ARAB EMTS"].plot(color='purple', linewidth=3)  # 按标签索引提取数据
g1.set_ylabel('USD (millions)', fontsize = 12)  # 设置Y轴标签
g1.set_xlabel('Year', fontsize = 12)  # 设置X轴标签
g1.set_title('对阿联酋出口的趋势', size = 15)  # 设置图表标题

plt.subplot(223)  # 选择子图位置
g2 = exp_country_table.loc["CHINA P RP"].plot(color='purple', linewidth=3)  # 按标签索引提取数据
g2.set_ylabel('USD (millions)', fontsize = 12)  # 设置Y轴标签
g2.set_xlabel('Year', fontsize = 12)  # 设置X轴标签
g2.set_title('对中国出口的趋势', size = 15)  # 设置图表标题

plt.subplot(224)  # 选择子图位置
g3 = exp_country_table.loc["HONG KONG"].plot(color='purple', linewidth=3)  # 按标签索引提取数据
g3.set_ylabel('USD (millions)', fontsize = 12)  # 设置Y轴标签
g3.set_xlabel('Year', fontsize = 12)  # 设置X轴标签
g3.set_title('对香港出口的趋势', size = 15)  # 设置图表标题

plt.subplots_adjust(hspace = 0.4)  # 调整子图间距
plt.savefig("5.png")  # 保存图形至文件

bold('**2010年至2018年印度对外贸易进口方向的趋势**')  # 以粗体Markdown格式输出:2010年至2018年印度对外贸易进口方向的趋势
plt.figure(figsize=(15,10))  # 创建图形画布
 
plt.subplot(221)  # 选择子图位置
g = imp_country_table.loc["U S A"].plot(color='coral', linewidth=3)  # 按标签索引提取数据
g.set_ylabel('USD (millions)', fontsize = 12)  # 设置Y轴标签
g.set_xlabel('Year', fontsize = 12)  # 设置X轴标签
g.set_title('从美国进口的趋势', size = 15)  # 设置图表标题

plt.subplot(222)  # 选择子图位置
g1 = imp_country_table.loc["U ARAB EMTS"].plot(color='coral', linewidth=3)  # 按标签索引提取数据
g1.set_ylabel('USD (millions)', fontsize = 12)  # 设置Y轴标签
g1.set_xlabel('Year', fontsize = 12)  # 设置X轴标签
g1.set_title('从阿联酋进口的趋势', size = 15)  # 设置图表标题

plt.subplot(223)  # 选择子图位置
g2 = imp_country_table.loc["CHINA P RP"].plot(color='coral', linewidth=3)  # 按标签索引提取数据
g2.set_ylabel('USD (millions)', fontsize = 12)  # 设置Y轴标签
g2.set_xlabel('Year', fontsize = 12)  # 设置X轴标签
g2.set_title('从中国进口的趋势', size = 15)  # 设置图表标题

plt.subplot(224)  # 选择子图位置
g3 = imp_country_table.loc["SAUDI ARAB"].plot(color='coral', linewidth=3)  # 按标签索引提取数据
g3.set_ylabel('USD (millions)', fontsize = 12)  # 设置Y轴标签
g3.set_xlabel('Year', fontsize = 12)  # 设置X轴标签
g3.set_title('从沙特阿拉伯进口的趋势', size = 15)  # 设置图表标题

plt.subplots_adjust(hspace = 0.4)  # 调整子图间距
plt.savefig("6.png")  # 保存图形至文件

任务复盘|出口趋势分析(平台任务)

运行后核对:核对 df_exportdf_importexp_country_tableimp_country_table 是否按预测参与运算,实际输出是否与预测一致;若不一致,先检查类型、单位、索引/字段和运算顺序。

拓展练习:只改变一个关键输入或业务场景,先预测输出如何变化,再运行验证并解释变化原因。

进口趋势分析详解

展开进口趋势完整代码(默认收起)

# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行
bold('**2010年至2018年印度对外贸易进口方向的趋势**')  # 显示章节标题

# ==================== 创建2x2子图布局 ====================
fig, axes = plt.subplots(2, 2, figsize=(15, 10))  # 创建4个子图,用于展示4个主要进口来源国的趋势

# ==================== 绘制从美国进口趋势图 ====================
imp_country_table.loc['U S A'].plot(  # 选择从美国进口的数据
    ax=axes[0, 0],  # 绘制在第一行第一列
    color='coral',  # 设置线条颜色为珊瑚色,与出口的紫色形成对比
    linewidth=3  # 设置线宽
)
axes[0, 0].set_ylabel('USD (millions)', fontsize=12)  # 设置y轴标签
axes[0, 0].set_xlabel('Year', fontsize=12)  # 设置x轴标签
axes[0, 0].set_title('从美国进口的趋势', size=15)  # 设置子图标题

# ==================== 绘制从阿联酋进口趋势图 ====================
imp_country_table.loc['U ARAB EMTS'].plot(  # 选择从阿联酋进口的数据
    ax=axes[0, 1],  # 绘制在第一行第二列
    color='coral',  # 使用珊瑚色,保持一致性
    linewidth=3  # 设置线宽
)
axes[0, 1].set_ylabel('USD (millions)', fontsize=12)  # 设置y轴标签
axes[0, 1].set_xlabel('Year', fontsize=12)  # 设置x轴标签
axes[0, 1].set_title('从阿联酋进口的趋势', size=15)  # 设置子图标题
# 继续完成同一幅进口趋势图的下半部分

# ==================== 绘制从中国进口趋势图 ====================
imp_country_table.loc['CHINA P RP'].plot(  # 选择从中国进口的数据
    ax=axes[1, 0],  # 绘制在第二行第一列
    color='coral',  # 使用珊瑚色
    linewidth=3  # 设置线宽
)
axes[1, 0].set_ylabel('USD (millions)', fontsize=12)  # 设置y轴标签
axes[1, 0].set_xlabel('Year', fontsize=12)  # 设置x轴标签
axes[1, 0].set_title('从中国进口的趋势', size=15)  # 设置子图标题

# ==================== 绘制从沙特阿拉伯进口趋势图 ====================
imp_country_table.loc['SAUDI ARAB'].plot(  # 选择从沙特阿拉伯进口的数据
    ax=axes[1, 1],  # 绘制在第二行第二列
    color='coral',  # 使用珊瑚色
    linewidth=3  # 设置线宽
)
axes[1, 1].set_ylabel('USD (millions)', fontsize=12)  # 设置y轴标签
axes[1, 1].set_xlabel('Year', fontsize=12)  # 设置x轴标签
axes[1, 1].set_title('从沙特阿拉伯进口的趋势', size=15)  # 设置子图标题

# ==================== 调整子图间距并显示 ====================
plt.subplots_adjust(hspace=0.4)  # 设置子图垂直间距
plt.tight_layout()  # 自动调整布局
plt.show()  # 显示图形

出口趋势:平台运行后待检验

印度贸易 CSV 用于教学平台固定练习。公开教材中的企业与行情拓展案例使用课程数据下载入口提供的财务报表和前复权股票日行情;下列问题帮助读者区分两类数据的分析对象:

  • 哪个目的地在各年出口额最高?排名是否稳定?
  • 阿联酋、中国与香港序列的增速和波动是否不同?
  • 当前字段只含贸易额时,不推断能源价格、转口贸易或双边关系等原因。
  • 只有平台运行输出附期间、单位、聚合表和来源快照后,才能把问题改写为条件式发现。

进口趋势:平台运行后待检验

请在平台数据成功读取后检验:

  • 各来源国年度进口额的排序、趋势和缺失年份;
  • 进口与出口是否在同一币种、价格口径和年度范围;
  • 当前国家—金额字段不能识别“高科技、军事装备、能源”货类,也不能推出油价因果;
  • 如果数据没有货类字段或价格指数,就只回答现有字段能够支持的问题,不延伸解释商品结构或价格变化。

本章小结

  • 透视表(pivot_table)是贸易数据分析的核心工具
  • 多子图布局(subplot)可同时对比多国趋势
  • 印度平台案例只演示透视表与多子图,伙伴排序须由实际输出验证
  • 出口与进口差异必须在同口径聚合表上比较,不能预写结论

随堂练习

  • 问题 1|分析前需要确认什么?:先确认中国主案例的数据、两家企业、分析期间和必需字段是否完整;数据可用只表示可以开始计算,不代表已经得到增长率或收益率。
  • 问题 2|需要完成哪些分析?:写出 2015—2025 单季营收和净利润的转换、去年同季配对、月度前复权收益、相邻两季营收同比转负情况和最终解释。若文件读取失败,应从课程数据下载入口重新下载并核对文件位置和字段。
  • 问题 3|结果说明什么?:用 2—3 句话解释港口企业营收/行情为何只是外贸景气代理,并指出不能推断的海关贸易结论。
  • 问题 4|两类案例有什么不同?:用印度数据制作一张透视表或趋势图,列出它与中国港口案例在分析对象、指标和时间单位上的三项差异。
  • 作答提示:请完成四道题,并在回答中引用实际运行结果;拓展练习中不要把课堂示例写成普遍规律或因果结论。
  • 学习说明:本章只练习代理指标的分析方法;即使数据可用,也不等同于完成领域等价的真实案例。

教师参考解答 1/4|输入、绑定与口径

教师参考解答|答案与说明 1

  • 所用数据与字段:规定资产 financial_statement 与学习要求一致的 stock_price_pre_adjusted;对象 601018.SH/600018.SH;字段 ts_code/end_date/total_revenue/n_income/trade_date/close
  • 数据准备答案:先确认公司、报告期和所需字段齐全,再计算季度增长率和月度复权收益。

教师参考解答|代码 1

展开代码(代码区可独立滚动)
from pathlib import Path  # 导入路径工具
import numpy as np  # 导入数值库以识别非有限财务值
import pandas as pd  # 导入表格库
root=Path('/home/ubuntu/r2_data_mount/data')  # 指定可用根目录
financial_path=root/'stock/financial_statement.h5'  # 绑定财务快照
price_path=root/'stock/stock_price_pre_adjusted.h5'  # 绑定前复权行情并保持章节口径一致
if not financial_path.exists() or not price_path.exists(): raise FileNotFoundError('未找到课程数据文件,请从课程数据下载入口获取并核对文件位置')  # 缺失即停止
target_codes=['601018.SH','600018.SH']  # 锁定宁波港与上港集团
target_raw_codes=[code.replace('.SH','.XSHG').replace('.SZ','.XSHE') for code in target_codes]  # 转换为HDF原始证券代码
financials=pd.read_hdf(financial_path,key='financial_data',where='order_book_id in target_raw_codes',columns=['order_book_id','quarter','info_date','operating_revenue','net_profit']).rename(columns={'order_book_id':'ts_code','quarter':'end_date','info_date':'ann_date','operating_revenue':'total_revenue','net_profit':'n_income'})  # 读取财务流量与披露日期
prices=pd.read_hdf(price_path,key='data',where='order_book_id in target_raw_codes',columns=['close']).reset_index().rename(columns={'order_book_id':'ts_code','date':'trade_date'})  # 按已核验key读取行情必需字段
for frame in (financials,prices): frame['ts_code']=frame['ts_code'].str.replace('.XSHG','.SH',regex=False).str.replace('.XSHE','.SZ',regex=False)  # 统一课堂证券代码后缀
financials['end_date']=pd.PeriodIndex(financials['end_date'].str.upper(),freq='Q').end_time.normalize()  # 将季度键转换为报告期末日期
financials['ann_date']=pd.to_datetime(financials['ann_date'],errors='coerce')  # 将披露日期转换为可排序时间
prices['trade_date']=pd.to_datetime(prices['trade_date'])  # 统一交易日期
version_counts=financials.groupby(['ts_code','end_date']).size().rename('version_rows')  # 统计每个公司报告期披露版本数
financials=financials.sort_values(['ts_code','end_date','ann_date']).drop_duplicates(['ts_code','end_date'],keep='last')  # 以最晚披露日固定唯一版本
financials=financials.loc[financials['end_date'].between('2015-01-01','2025-12-31')].copy()  # 严格保留2015—2025报告期
prices=prices.loc[prices['trade_date'].between('2015-01-01','2025-12-31')].copy()  # 严格保留2015—2025交易日

教师参考解答 2/4|累计值转换为单季值

教师参考解答|代码 1

展开代码(代码区可独立滚动)
financials=financials.sort_values(['ts_code','end_date']).copy()  # 固定公司与报告期顺序
financials['fiscal_year']=financials['end_date'].dt.year  # 从报告期提取财年键
financials['fiscal_quarter']=financials['end_date'].dt.quarter  # 从报告期提取财季键
group_keys=['ts_code','fiscal_year']  # 定义累计值只能在同公司同财年内相减
previous_quarter=financials.groupby(group_keys)['fiscal_quarter'].shift(1)  # 取得同财年上一条财季
is_q1=financials['fiscal_quarter'].eq(1)  # 第一季度累计值即单季值
has_adjacent_prior=financials['fiscal_quarter'].sub(previous_quarter).eq(1)  # 检查Q2至Q4是否有相邻前季
for cumulative_name,quarter_name in [('total_revenue','quarter_revenue'),('n_income','quarter_net_profit')]:  # 分别转换营收和净利润累计值
    prior_cumulative=financials.groupby(group_keys)[cumulative_name].shift(1)  # 取得同财年上一季累计值
    financials[quarter_name]=financials[cumulative_name].where(is_q1,financials[cumulative_name]-prior_cumulative)  # Q1保留、其余季度作差
    financials.loc[~is_q1&~has_adjacent_prior,quarter_name]=np.nan  # 前季断档时禁止伪造单季值
single_quarter_audit=financials.groupby('ts_code').agg(rows=('end_date','size'),missing_quarter_revenue=('quarter_revenue',lambda series:int(series.isna().sum())),missing_quarter_net_profit=('quarter_net_profit',lambda series:int(series.isna().sum())))  # 汇总转换缺口
print(version_counts.groupby(level=0).agg(['count','max']),single_quarter_audit)  # 输出版本数量与单季转换检查

教师参考解答|答案与说明 1

解释答案:利润表流量通常为年内累计口径;Q2—Q4 必须减去同财年上一季累计值,Q1 才可直接视为单季值。如果缺少相邻季度或无法确定披露版本,应说明缺少哪些记录,不能把累计值直接比较为“连续两季增长”。

教师参考解答 3/4|去年同季配对与检查

教师参考解答|代码 1

展开代码(代码区可独立滚动)

# 用财年与财季精确对齐去年同季,禁止按位置取第四条旧记录
prior_year_financials=financials[['ts_code','fiscal_year','fiscal_quarter','quarter_revenue','quarter_net_profit']].rename(columns={'fiscal_year':'prior_fiscal_year','fiscal_quarter':'prior_fiscal_quarter','quarter_revenue':'prior_quarter_revenue','quarter_net_profit':'prior_quarter_net_profit'})  # 构造去年同季单季值候选表
prior_year_financials['fiscal_year']=prior_year_financials['prior_fiscal_year']+1  # 将去年记录对齐到本年连接键
prior_year_financials['fiscal_quarter']=prior_year_financials['prior_fiscal_quarter']  # 保持同一财季才允许配对
financials=financials.merge(prior_year_financials,on=['ts_code','fiscal_year','fiscal_quarter'],how='left',validate='one_to_one',indicator='prior_year_join')  # 用公司与日历键精确连接
financials['has_prior_year_counterpart']=financials['prior_year_join'].eq('both')  # 标记去年同季记录是否存在
financials['has_calendar_pair_mismatch']=financials['has_prior_year_counterpart']&((financials['prior_fiscal_year'].ne(financials['fiscal_year']-1))|financials['prior_fiscal_quarter'].ne(financials['fiscal_quarter']))  # 检查配对是否严格为去年同季
assert not financials['has_calendar_pair_mismatch'].any()  # 禁止发布任何错配同比
financials['is_revenue_yoy_computable']=financials['has_prior_year_counterpart']&np.isfinite(financials['quarter_revenue'])&np.isfinite(financials['prior_quarter_revenue'])&financials['prior_quarter_revenue'].ne(0)  # 仅允许有限单季营收与非零分母
financials['is_profit_yoy_computable']=financials['has_prior_year_counterpart']&np.isfinite(financials['quarter_net_profit'])&np.isfinite(financials['prior_quarter_net_profit'])&financials['prior_quarter_net_profit'].ne(0)  # 仅允许有限单季净利润与非零分母
financials['revenue_yoy']=(financials['quarter_revenue']/financials['prior_quarter_revenue']-1).where(financials['is_revenue_yoy_computable'])  # 计算单季营收同比
financials['profit_yoy']=(financials['quarter_net_profit']/financials['prior_quarter_net_profit']-1).where(financials['is_profit_yoy_computable'])  # 计算单季净利润同比
financials['missing_prior_year_counterpart']=~financials['has_prior_year_counterpart']  # 标记无去年同季的不可计算期
financials['revenue_yoy_invalid_value']=financials['has_prior_year_counterpart']&(~np.isfinite(financials['quarter_revenue'])|~np.isfinite(financials['prior_quarter_revenue'])|financials['prior_quarter_revenue'].eq(0))  # 检查单季营收非有限值与零分母
financials['profit_yoy_invalid_value']=financials['has_prior_year_counterpart']&(~np.isfinite(financials['quarter_net_profit'])|~np.isfinite(financials['prior_quarter_net_profit'])|financials['prior_quarter_net_profit'].eq(0))  # 检查单季利润非有限值与零分母
financials['revenue_yoy_uncomputable']=~financials['is_revenue_yoy_computable']  # 标记营收同比未通过检查要求的期间
financials['profit_yoy_uncomputable']=~financials['is_profit_yoy_computable']  # 标记净利润同比未通过检查要求的期间
financials['quarter_rows']=1  # 为公司级检查统计期数
yoy_pair_audit=financials.groupby('ts_code',as_index=False)[['quarter_rows','has_prior_year_counterpart','missing_prior_year_counterpart','is_revenue_yoy_computable','revenue_yoy_invalid_value','revenue_yoy_uncomputable','is_profit_yoy_computable','profit_yoy_invalid_value','profit_yoy_uncomputable','has_calendar_pair_mismatch']].sum()  # 汇总有效、缺失、无效值与错配数

教师参考解答|答案与说明 1

另行核对:随机抽一家公司和两个报告期,用原始累计值按日历键手算去年同季同比;断档、零分母或非有限值必须与检查列一致。

教师参考解答 4/4|行情、提示与说明

教师参考解答|代码 1

展开代码(代码区可独立滚动)

# 将两行负增长满足条件限定为相邻日历季度,断档样本请先检查数据后再继续
monthly_close=prices.set_index('trade_date').groupby('ts_code')['close'].resample('ME').last().unstack(0)  # 生成月末复权价格
monthly_return=monthly_close.pct_change(fill_method=None)  # 计算月度复权收益率
financials['calendar_quarter_id']=financials['fiscal_year']*4+financials['fiscal_quarter']  # 编码连续季度并覆盖跨年Q4到Q1
financials['previous_calendar_quarter_id']=financials.groupby('ts_code')['calendar_quarter_id'].shift(1)  # 取当前观测的上一条季度键
financials['is_adjacent_to_previous']=financials['calendar_quarter_id'].sub(financials['previous_calendar_quarter_id']).eq(1)  # 仅接受相邻日历季度
financials['previous_revenue_yoy']=financials.groupby('ts_code')['revenue_yoy'].shift(1)  # 取上一相邻候选期的精确同比
financials['has_two_adjacent_negative_yoy']=financials['is_adjacent_to_previous']&financials['revenue_yoy'].lt(0)&financials['previous_revenue_yoy'].lt(0)  # 仅在相邻两季且同比均可计算为负时满足条件
condition_met=financials.groupby('ts_code')['has_two_adjacent_negative_yoy'].any()  # 汇总公司级情景核对是否满足条件
assert set(target_codes)<=set(financials['ts_code']) and set(target_codes)<=set(prices['ts_code'])  # 核对两家企业完整性
print(yoy_pair_audit,financials[['ts_code','end_date','revenue_yoy','profit_yoy','has_prior_year_counterpart','is_adjacent_to_previous']],monthly_return.tail(),condition_met)  # 输出配对检查、指标与检查结果

教师参考解答|答案与说明 1

  • 解释答案:港口公司营收和行情同时受公司治理、资本开支、费率与市场风险影响,只是外贸景气代理,不能推出中国海关贸易总额、伙伴结构或货类因果变化。
  • 对照答案:平台提供且授权印度表后,沿 lst-ch25-india-comparison-route 读取 country/year/value,分别构造“国家 × 年度”的出口额与进口额透视表,再绘制所选国家趋势;对象是国家—商品贸易而非上市公司,指标是进出口额而非营收/收益率,时间粒度是年度而非季度/月度。
  • 参考结果:两家公司及其报告期和字段、去年同季配对情况、季度营业收入与利润同比、月度前复权收益,以及印度贸易表按国家和年份制作的透视表。若缺少去年同季、分母为零或样本中断,应在结果中说明原因,不计算没有意义的增长率。
  • 边界 / 局限:跨国口径和币种不可直接比较
  • 常见错误:把外国结果拓展应用成中国结论;切换到后复权文件而不说明;把累计财报值直接当单季流量